package com.xyz.aop;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xyz.util.RedisUtil;
import com.xyz.util.ResponseUtil;
import com.xyz.util.dto.DataResult;
import com.xyz.util.dto.LoginInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 用户登录校验
 */
@Component // 声明这是一个组件
@Aspect // 声明这是一个切面Bean
@Order(1) // 优先级
public class LoginAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoginAspect.class);

    @Autowired
    private RedisUtil redis;

    // 登录后用户身份请求头
    private final static String HEAD_TOKEN = "Authorization";

    // 排除接口
    public final static List<String> urlList = Arrays.asList(
            "/system/passwordLessLogin", "/system/login", "/system/appLogin", "/system/WeChatLogin", "/system/uploadFile", "/system/readFile",
            "/selectTest", "/insertTest", "/updateTest", "/deleteTest", "/externalPost/selectLinearData",
            "/externalPost/selectSubtotal", "/externalPost/selectEquipment", "/externalPost/selectRo", "/externalPost/selectTimeByEmCode"
    );

    // 配置切入点，该方法无方法体，主要为方便同类中其他方法使用此处配置的切入点
    @Pointcut("execution(* com.xyz.controller..*.*(..))")
    public void packet() {
    }

    // 配置环绕通知
    @Around("packet()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取request response对象
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

        if (
                urlList.contains(request.getRequestURI()) ||
                request.getRequestURI().contains("/system/readFile") ||
                request.getRequestURI().contains("/message/connect") ||
                request.getRequestURI().contains("/mqtt")
        ) {
            return joinPoint.proceed();
        }

        String token = request.getHeader(HEAD_TOKEN);
        if (StrUtil.isBlank(token)) { // token为空
            logger.warn("用户登录权限接口请求头中无token");
            if (response != null) {
                ResponseUtil.outWithJson(response, DataResult.build9300()); // 未登录
            }
            return null;
        }
        String tokenValue = redis.get(token);
        if (StrUtil.isBlank(tokenValue)) { // token不为空但redis无
            logger.warn("用户登录权限接口请求头中的token不在Redis");
            if (response != null) {
                ResponseUtil.outWithJson(response, DataResult.build9400()); // 登录过期
            }
            return null;
        }
        redis.setOutTime(token, 30, TimeUnit.DAYS);

        // 注入LoginInfo
        LoginInfo li = JSONUtil.toBean(tokenValue, LoginInfo.class);

        Object[] args = joinPoint.getArgs();
        args[0] = li;
        return joinPoint.proceed(args);
    }

}
